<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ThreeJS Logo Shader</title>
    <style>
      html,
      body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        overflow: hidden;
        background: #000;
      }
    </style>
  </head>
  <body>
    <!-- 裸模块路径映射 -->
    <script type="importmap">
      {
        "imports": {
          "three": "./threejs/build/three.module.js",
          "three/addons/": "./threejs/examples/jsm/"
        }
      }
    </script>
    <script type="module">
      import * as THREE from "three";
      import { OrbitControls } from "three/addons/controls/OrbitControls.js";
      import { GUI } from "three/addons/libs/lil-gui.module.min.js";

      /* ---------------- 基础场景 ---------------- */
      const scene = new THREE.Scene();
      scene.fog = new THREE.Fog(0x000000, 10, 50);

      const camera = new THREE.PerspectiveCamera(
        50,
        innerWidth / innerHeight,
        0.1,
        1000
      );
      camera.position.set(0, 0, 10);

      const renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true,
      });
      renderer.setSize(innerWidth, innerHeight);
      renderer.setPixelRatio(devicePixelRatio);
      document.body.appendChild(renderer.domElement);

      const controls = new OrbitControls(camera, renderer.domElement);
      controls.enableDamping = true;

      /* ---------------- Shader ---------------- */
      const vertexShader = `
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vNormal;
void main() {
  vUv = uv;
  vPosition = position;
  vNormal = normalize(normalMatrix * normal);
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}`;

      const fragmentShader = `
uniform float time;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vNormal;
void main() {
  float noise = sin(vPosition.x * 2.0 + time) * 0.25
              + cos(vPosition.y * 2.0 + time * 0.5) * 0.25
              + sin(vPosition.z * 2.0 + time * 0.8) * 0.5;
  float fresnel = pow(1.0 - abs(dot(vNormal, vec3(0.0, 0.0, 1.0))), 2.0);
  vec3 baseColor = mix(color1, color2, noise + 0.5);
  vec3 glowColor = mix(baseColor, color3, fresnel);
  float pulse    = (sin(time * 2.0) + 1.0) * 0.15 + 0.85;
  gl_FragColor   = vec4(glowColor * pulse, 1.0);
}`;

      const material = new THREE.ShaderMaterial({
        uniforms: {
          time: { value: 0 },
          color1: { value: new THREE.Color(0xdbb8ff) },
          color2: { value: new THREE.Color(0x98d0fb) },
          color3: { value: new THREE.Color(0xfdebbf) },
        },
        vertexShader,
        fragmentShader,
        side: THREE.DoubleSide,
      });

      /* ---------------- Logo 几何体 ---------------- */
      function createThreeJSLogoGeometry() {
        const v = new THREE.Vector2(0, 1);
        const c = new THREE.Vector2();
        const s = 0.85;

        const tri = [
          v.clone().multiplyScalar(s).add(new THREE.Vector2(0, -0.25)),
          v
            .clone()
            .rotateAround(c, (-Math.PI * 2) / 3)
            .multiplyScalar(s)
            .add(new THREE.Vector2(0, -0.25)),
          v
            .clone()
            .rotateAround(c, (Math.PI * 2) / 3)
            .multiplyScalar(s)
            .add(new THREE.Vector2(0, -0.25)),
        ];
        const triFlip = [
          v
            .clone()
            .rotateAround(c, Math.PI)
            .multiplyScalar(s)
            .sub(new THREE.Vector2(0, -0.25)),
          v
            .clone()
            .rotateAround(c, Math.PI / 3)
            .multiplyScalar(s)
            .sub(new THREE.Vector2(0, -0.25)),
          v
            .clone()
            .rotateAround(c, -Math.PI / 3)
            .multiplyScalar(s)
            .sub(new THREE.Vector2(0, -0.25)),
        ];

        const holes = [];
        const hA = (3 / Math.sqrt(3)) * 0.5;
        for (let row = 0; row < 4; row++) {
          const items = 1 + row * 2;
          const h = 1.5 * (1.5 - row);
          const w = -((items - 1) / 2) * hA;
          for (let i = 0; i < items; i++) {
            const offsetX = w + hA * i;
            const offsetY = h;
            const pts = (i % 2 ? triFlip : tri).map((p) =>
              p.clone().add(new THREE.Vector2(offsetX, offsetY))
            );
            holes.push(new THREE.Path(pts));
          }
        }

        const contour = [
          v.clone().multiplyScalar(4.1).add(new THREE.Vector2(0, -1)),
          v
            .clone()
            .rotateAround(c, (Math.PI * 2) / 3)
            .multiplyScalar(4.1)
            .add(new THREE.Vector2(0, -1)),
          v
            .clone()
            .rotateAround(c, (-Math.PI * 2) / 3)
            .multiplyScalar(4.1)
            .add(new THREE.Vector2(0, -1)),
        ];
        const shape = new THREE.Shape(contour);
        shape.holes = holes;

        const geo = new THREE.ExtrudeGeometry(shape, {
          depth: 0.1,
          bevelEnabled: false,
        });
        geo.rotateZ(Math.PI * 0.25);
        geo.center();
        return geo;
      }

      const mesh = new THREE.Mesh(createThreeJSLogoGeometry(), material);
      scene.add(mesh);

      /* ---------------- GUI ---------------- */
      const gui = new GUI({ title: "Color Palette", width: 250 });
      gui.addColor(material.uniforms.color1, "value").name("Color 1");
      gui.addColor(material.uniforms.color2, "value").name("Color 2");
      gui.addColor(material.uniforms.color3, "value").name("Color 3");
      gui.close(); // 默认折叠

      /* ---------------- 自适应 ---------------- */
      addEventListener("resize", () => {
        camera.aspect = innerWidth / innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(innerWidth, innerHeight);
      });

      /* ---------------- 动画 ---------------- */
      function animate() {
        requestAnimationFrame(animate);
        material.uniforms.time.value += 0.01;
        controls.update();
        renderer.render(scene, camera);
      }
      animate();

      /* ---------------- 水印 ---------------- */
      console.log(
        "%cThreeJS Logo Shader © 2024",
        "color:#8ff;font-size:14px;background:#000;padding:4px 8px;border-radius:4px;"
      );
    </script>
  </body>
</html>
